#pragma once

#include <string>
#include <vector>

#include <wayland-client.h>
#include <wayland-egl.h>

struct WindowProperties
{
    WindowProperties(int w, int h, bool f, intptr_t v)
        : width(w)
        , height(h)
        , fullscreen(f)
        , visual_id(v)
    {
    }
    WindowProperties()
        : width(0)
        , height(0)
        , fullscreen(false)
        , visual_id(0)
    {
    }

    int width;
    int height;
    bool fullscreen;
    intptr_t visual_id;
};

class WaylandWorker
{
public:
    WaylandWorker();
    ~WaylandWorker();

    bool init_display();
    void cleanup_display();
    bool create_window(WindowProperties const &properties);

    bool should_quit();

private:
    static void quit_handler(int signum);

    static const struct wl_registry_listener registry_listener_;
    static const struct wl_shell_surface_listener shell_surface_listener_;
    static const struct wl_output_listener output_listener_;
    static const struct wl_seat_listener seat_listener_;

    static void registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version);
    static void registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t name);

    static void seat_handle_name(void *data, struct wl_seat *wl_seat, const char *name);
    static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, uint32_t caps);

    static void output_handle_geometry(void *data,
                                       struct wl_output *wl_output,
                                       int32_t x,
                                       int32_t y,
                                       int32_t physical_width,
                                       int32_t physical_height,
                                       int32_t subpixel,
                                       const char *make,
                                       const char *model,
                                       int32_t transform);
    static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh);
    static void output_handle_done(void *data, struct wl_output *wl_output);
    static void output_handle_scale(void *data, struct wl_output *wl_output, int32_t factor);

    static void shell_surface_handle_ping(void *data, struct wl_shell_surface *shell_surface, uint32_t serial);
    static void shell_surface_handle_configure(void *data, struct wl_shell_surface *shell_surface, uint32_t edges, int32_t width, int32_t height);
    static void shell_surface_handle_popup_done(void *data, struct wl_shell_surface *shell_surface);

    struct my_output
    {
        wl_output *output;
        int32_t width, height;
        int32_t refresh;
    };

    struct seat_info
    {
        wl_seat *seat;

        struct wl_keyboard *keyboard;
        uint32_t capabilities;
        char *name;

        int32_t repeat_rate;
        int32_t repeat_delay;
    };

    struct my_display
    {
        wl_display *display;
        wl_registry *registry;
        wl_compositor *compositor;
        wl_shell *shell;
        std::vector<struct my_output *> outputs;
        seat_info *seatInfo;
    } * display_;

    struct my_window
    {
        WindowProperties properties;
        struct wl_surface *surface;
        struct wl_egl_window *native;
        struct wl_shell_surface *shell_surface;
    } * window_;

private:
    static volatile bool should_quit_;

    std::vector<std::string> m_interfaces;
};
